package com.me.experiment;

import java.util.ArrayList;

import com.badlogic.gdx.Graphics;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.me.experiment.Animation.Frame;



public abstract class Object {
	
	Object parent_;
	ArrayList<Object> children_;
	ArrayList<Animation> animations_;
	
	// Position / dimensions
	protected float x_; // x_, y_ positions relative to parents
	protected float y_;
	float z_;
	protected float width_;
	protected float height_; 
	// Flags
	public boolean visible_;
	public boolean touchable_;
	public boolean touched_;
	
	boolean movable_; 
	boolean play_sound ;
	boolean attachedtoParent_ = false;

	
	// type / statut
	int animationid;
	
	// Sound
	Sound sound_;
	Scene scene_;
	
	float onsetTime_;
	float offsetTime_;

	
	/**
	 * Constructor 
	 */
	public Object() {
		
		
		parent_ = null;
		children_ = new ArrayList<Object>();
		animations_ = new ArrayList<Animation>();
		animationid = 0;
		
		x_ = 10.0f + (float)Math.random()*300.0f; // test
		y_ = 10.0f + (float)Math.random()*700.0f;
		z_ = 0.0f;

		visible_ = true;
		touchable_ = true;
		movable_ = true;
		
		sound_ = null;
		play_sound = false;
		
		

	}
	
	/**
	 * Sets the position of the object
	 * @param x	 * 	abscisse of object
	 * @param y  *  ordinate of object
	 */
	public void setPosition(float x, float y) {
		x_ = x; 
		y_ = y; 
	}	
	
	public void setDimensions(float w, float h) {
		width_ = w; 
		height_ = h; 
	}	
	
	
	
	/**
	 * Sets the depth of the object
	 * @param z	 * 	depth of object
	 */
	public void setDepth(float z) {
		z_ = z;
	}

	/**
	 * Attach a child to the object
	 * @param o	 * 	child object
	 */
	public Object attachChild(Object o) {
		o.parent_ = this;
		children_.add(o);
		return o;
	}
	
	/**
	 * When objects are tied by family link, finds the root
	 * @return root object
	 */	
	public Object getMoveRoot(){
		if (this.attachedtoParent_){
			return this.parent_.getMoveRoot();
		}else{
			return this;
		}
	}
	
	
	/**
	 * Adds a frame to the object
	 * @param p	 * 	Frame to add
	 * @see Pixmap
	 */
	public void addSingleFrame(Pixmap p) {
		animations_ = null;
		animations_ = new ArrayList<Animation>();
		animations_.add(new Animation());
		Animation a = animations_.get(animations_.size()-1);
		a.name_ = "Static";
		a.addFrame(p, 0.0f);
		width_ = p.getWidth();
		height_ = p.getWidth();

	}
	
	/**
	 * Changes frames of an object
	 * @param p	 * 	frames
	 * @see Pixmap
	 */
	public void changeSingleFrame(Pixmap p) {
		animations_.get(0).frames_.get(0).pixmap_ = p;
	}
	
	
	/**
	 * Adds a sound to an object
	 * @param s	 * 	Sound
	 * @see Sound
	 */
	public void addSingleSound(Sound s) {
		sound_ = s;
	}
	
	
	/**
	 * Adds animation to an object
	 * @param String * 	name of animation
	 * @see Animation
	 */
	public Animation addAnimation(String name) {
		animations_.add(new Animation());
		Animation a = animations_.get(animations_.size()-1);
		a.name_ = name;

		return a;
	}
	
	
	/**
	 * Updates relative position of an object and of its children
	 * @param x	 * 	abscissa
	 * @param y  *  ordinate
	 */
	public void updatePosition(float x, float y) {
		if (movable_) {
			for (Object o : children_) {
				o.updatePosition(o.x_ + (x-x_ ), o.y_ + (y-y_ ));
			}
			// Update self position
			x_ = x; 
			y_ = y; 			
		}
	}
	
	/**
	 * Updates absolute position of an object and of relative position of  its children
	 * @param x	 * 	abscissa
	 * @param y  *  ordinate
	 */
	public void updateAbsPosition(float x, float y) {
		if (movable_) {
			if (parent_ != null){
			x_ = x - parent_.getAbsx();
			y_ = y - parent_.getAbsy();
			} else {
				x_ = x;
				y_ = y;
			}
				
		}
	}
	
	/**
	 * Returns absolute position of an object
	 * @return absolute abscissa
	 */
	public float getAbsx(){
		if (parent_ == null){
			return x_;
		} else {		
			return parent_.getAbsx() + x_;
		}
	}

	/**
	 * Returns absolute position of object
	 * @return object absolute ordinate
	 * @param y  * absolute ordinate
	 */
	public float getAbsy(){
		if (parent_ == null){
			return y_;
		} else {		
			return parent_.getAbsy() + y_;
		}
	}

	
	/**
	 * Return if object is touched
	 * @param x	 * 	abscissa of touch event
	 * @param y  *  ordinate of touch event
	 * @return boolean touched or not
	 */
	public boolean istouch(float x,float y){
		return (x >= this.getAbsx() && x <= this.getAbsx()+this.width_ && y >= this.getAbsy() && y <= this.getAbsy()+this.height_);
	}

	/**
	 * Scales the dimension of images of an object by updating the size of images
	 * @param r	 * 	scaling ration
	 */
	public void scaleDimensions(float r) {
		updateDimensions();
		for (Animation a : animations_) {
			for (Frame f : a.frames_) {
				f.width_ *= r;
				f.height_ *= r;
			}
		}
	}
	
	/**
	 * Forces the dimensions of images of an object
	 * @param w  * 	width
	 * @param y  *  height
	 */
	public void forceDimensions(int w, int h) {
		// if a dimension is 0, then it is calculated from the pixmap ratio
		updateDimensions();
		Animation a = animations_.get(animationid);
			for (Frame f : a.frames_) {
				float ratio = (float) f.width_/ (float) f.height_;
				f.width_ = w;
				f.height_ = h;
				if (w == 0) 	f.width_ = (int) ((float) h * ratio);
				if (h == 0) 	f.height_ = (int) ((float) w / ratio);
			}
		
		
	}
	
	
	/**
	 * Updates frame dimensions
	 * @see Frame
	 */
	public void updateDimensions() {
		Animation a = animations_.get(animationid);
			for (Frame f : a.frames_) {
				if (f.width_ < f.pixmap_.getWidth())		f.width_ = f.pixmap_.getWidth();
				if (f.height_ < f.pixmap_.getHeight())	f.height_ = f.pixmap_.getHeight();
			}
		
	}
	
	/**
	 * Sets object dimension to the size of a Pixmap
	 * @param p	 * 	Pixmap
	 * @see Pixmap
	 */
	public void setObjDimensionAs(Pixmap p) {
		this.width_ = (float)p.getWidth();
		this.height_ = (float)p.getHeight();
	}

	/**
	 * Response to a TOUCH DOWN event on object
	 */
	protected abstract void onTouchDown();
	/**
	 * Response to a TOUCH UP event on object
	 */
	protected abstract void onTouchUp();
	/**
	 * Response to a LONG TOUCH event on object
	 */
	protected abstract void onTouchLong(); // param delay dans object
	/**
	 * Response to a DRAG event on object
	 */
	protected abstract void onTouchDrag(float x,float y);
	
	/**
	 * Update of object
	 * @param deltatime time passed since previous loop  
	 */
	protected abstract void update(float deltatime);
	
	/**
	 * Draws object
	 * @param batch 
	 * @param g Graphics of the experiment on which to draw 
	 * @see Graphics 
	 */	
	protected abstract void draw(SpriteBatch batch);
	
	
	public void show(){
		visible_ = true;
		touchable_ = true;
	}
	
	
	public void show(float duration){
		if (visible_ = false){
			onsetTime_ = scene_.getTime();
			offsetTime_ = onsetTime_ + duration;
			visible_ = true;
			touchable_ = true;
		}
		if (scene_.getTime()>offsetTime_){
			hide();
		}
		
		
		
	}
	
	public void hide(){
		visible_ = false;
		touchable_ = false;
		
	}
	
	
}
